function hslToRgb(h, s, l) {
	let r, g, b;
	if (s == 0){
		r = g = b = l; // achromatic
	}else{
		const hue2rgb = function hue2rgb(p, q, t) {
			if (t < 0) t += 1;
			if (t > 1) t -= 1;
			if (t < 1 / 6) return p + (q - p) * 6 * t;
			if (t < 1 / 2) return q;
			if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
			return p;
		}
		const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
		const p = 2 * l - q;
		r = hue2rgb(p, q, h + 1 / 3);
		g = hue2rgb(p, q, h);
		b = hue2rgb(p, q, h - 1 / 3);
	}
	return [r, g, b];
}





/* 初始化着色器 */
async function initShadersPrograms(gl, obj){
	console.group("initShadersPrograms().vertShaders")
	const vertShaders = {};
	for (const [name, path] of Object.entries(obj.vertexShaders))
		vertShaders[name] = await Shader.fromFile(
			gl, gl.VERTEX_SHADER,
			name, path
		);
	console.groupEnd("initShadersPrograms().vertShaders")
	
	console.group("initShadersPrograms().fragShaders")
	const fragShaders = {};
	for (const [name, path] of Object.entries(obj.fragmentShaders))
		fragShaders[name] = await Shader.fromFile(
			gl, gl.FRAGMENT_SHADER,
			name, path
		);
	console.groupEnd("initShadersPrograms().fragShaders")
	
	console.group("initShadersPrograms().programs")
	const programs = {};
	for (const [name, info] of Object.entries(obj.programs))
		if (typeof info == "string"){
			const {vertexShader, fragmentShader, program} = await ShaderProgram.fromFile(gl, info);
			vertShaders[name] = vertexShader;
			fragShaders[name] = fragmentShader;
			programs[name] = program;
		}else{
			programs[name] = new ShaderProgram(
				gl, name, vertShaders[info.shaders[0]],
				fragShaders[info.shaders[1]],
				{
					attributes: info.attributes,
					uniforms: info.uniforms
				}
			)
		}
	console.groupEnd("initShadersPrograms().programs")
	
	return [vertShaders, fragShaders, programs];
}


//初始化
let vertShaders, fragShaders, programs;
//const stars = [];
let starsAddition = []; //附加star 在渲染时加入
const STARS_MAX = 100;
async function init(gl){
	[vertShaders, fragShaders, programs] = await initShadersPrograms(gl, {
		vertexShaders: {},
		fragmentShaders: {},
		programs: {
			particle: "./shader/particle.program"
		}
	});
	console.log("[init]", vertShaders, fragShaders, programs);
	
	/* 视口调整 */
	window.addEventListener("resize", ()=>resize(gl));
	resize(gl);
}


/* 大小调整 */
function resize(gl){
	console.log("resize")
	
	/* resize */
	const width = innerWidth;//1920;
	const height = innerHeight;//1080;
	
	//canvas宽高
	gl.canvas.width = width;
	gl.canvas.height = height;
	
	//重置视域
	gl.viewport(0, 0, width, height);
	
	programs.particle.useSafe(()=>{
		programs.particle.setUniform("uWidth", "uniform1f", width);
		programs.particle.setUniform("uHeight", "uniform1f", height);
	});
}


//VAO
const vao = {};
const buffers = {};
function init_vao(gl){
	vao.particle = new VertexArray(gl).bindSafe(()=>{
		const list = {
			position: {name: "inPosition", size: 3},
			startTime: {name: "inStartTime", size: 1},
			velocity: {name: "inVelocity", size: 3},
			duration: {name: "inDuration", size: 1},
			color: {name: "inColor", size: 4}
		};
		
		for (const [attrName, {name, size}] of Object.entries(list)){
			buffers[attrName] = new Buffer(gl,
				new Float32Array(STARS_MAX * size),
				{usage: gl.DYNAMIC_DRAW}
			);
			programs.particle.setAttributeBuffer(
				name,
				buffers[attrName],
				{size, type: gl.FLOAT}
			);
		}
	});
}


/* 开启渲染循环 */
function startUpdate(gl, callback=()=>{}){
	//const stats = new Stats(); //统计
	//document.body.append(stats.domElement);
	
	//渲染设置
	gl.clearColor(0, 0, 0, 0); //背景
	//gl.clear(gl.COLOR_BUFFER_BIT);
	gl.enable(gl.BLEND); //混合（透明）
	gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
	
	//渲染循环
	let last = true;
	const render = (time) => {
		//stats.update();
		requestAnimationFrame(render);
		last = !last;
		if (last) return;
		
		update(gl, time);
		callback(time);
	};
	requestAnimationFrame(render);
}

//渲染
const t0 = +new Date();
let lastIndex = 0;
function update(gl, time){
	gl.clear(gl.COLOR_BUFFER_BIT); //清除
	
	programs.particle.use();
	programs.particle.setUniform("uTime", "uniform1f", +new Date()-t0);
	vao.particle.bind(); //切换VAO
	
	const positions = [];
	const startTimes = [];
	const velocities = [];
	const durations = [];
	const colors = [];
	for (const star of starsAddition){
		positions.push(...star.pos);
		startTimes.push(star.startTime);
		velocities.push(...star.velocity);
		durations.push(star.duration);
		colors.push(...star.color);
	}
	if (lastIndex + starsAddition.length > STARS_MAX){
		const rest = STARS_MAX - lastIndex - 1;
		buffers.position.setSubData(new Float32Array(positions.slice(0, rest)), {offset: lastIndex * 3 * 4});
		buffers.startTime.setSubData(new Float32Array(startTimes.slice(0, rest)), {offset: lastIndex * 1 * 4});
		buffers.velocity.setSubData(new Float32Array(velocities.slice(0, rest)), {offset: lastIndex * 3 * 4});
		buffers.duration.setSubData(new Float32Array(durations.slice(0, rest)), {offset: lastIndex * 1 * 4});
		buffers.color.setSubData(new Float32Array(colors.slice(0, rest)), {offset: lastIndex * 4 * 4});
		
		buffers.position.setSubData(new Float32Array(positions.slice(rest)), {offset: lastIndex * 3 * 4});
		buffers.startTime.setSubData(new Float32Array(startTimes.slice(rest)), {offset: lastIndex * 1 * 4});
		buffers.velocity.setSubData(new Float32Array(velocities.slice(rest)), {offset: lastIndex * 3 * 4});
		buffers.duration.setSubData(new Float32Array(durations.slice(rest)), {offset: lastIndex * 1 * 4});
		buffers.color.setSubData(new Float32Array(colors.slice(rest)), {offset: lastIndex * 4 * 4});
		lastIndex += starsAddition.length - STARS_MAX;
	}else{
		buffers.position.setSubData(new Float32Array(positions), {offset: lastIndex * 3 * 4});
		buffers.startTime.setSubData(new Float32Array(startTimes), {offset: lastIndex * 1 * 4});
		buffers.velocity.setSubData(new Float32Array(velocities), {offset: lastIndex * 3 * 4});
		buffers.duration.setSubData(new Float32Array(durations), {offset: lastIndex * 1 * 4});
		buffers.color.setSubData(new Float32Array(colors), {offset: lastIndex * 4 * 4});
		lastIndex += starsAddition.length;
	}
	starsAddition = [];
	
	//console.log(stars.length, lastLen)
		/*const positions = [];
		const colors = [];
		if (stars.length > STARS_MAX)
			stars.splice(0, stars.length - STARS_MAX);
		
		for (let i=stars.length-1; i>=0; i--){
			const star = stars[i];
			const deltaTime = (+new Date() - star.startTime) / 1000;
			if (deltaTime >= star.duration)
				stars.splice(i, 1);
		}
		
		for (const star of stars){
			const deltaTime = (+new Date() - star.startTime) / 1000;
			positions.push(...star.pos.clone().add(
				star.velocity.clone().multipleScale(deltaTime)
			));
			colors.push(...star.color.clone().setA(1 - deltaTime / star.duration));
		}
		buffers.position.setSubData(new Float32Array(positions), {usage: gl.DYNAMIC_DRAW});
		buffers.color.setSubData(new Float32Array(colors), {usage: gl.DYNAMIC_DRAW});*/
	//console.log(buffers)
	
	gl.drawArrays(gl.POINTS, 0, STARS_MAX); //绘制
	//gl.drawArrays(gl.POINTS, 0, stars.length); //绘制
}

/* 主函数 */
async function main(gl){
	await init(gl);
	init_vao(gl);
	startUpdate(gl);
}

const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl2");
main(gl);


/* 事件部分 */
//添加粒子
function addStar(x, y,
	velocity=new Vector3(Math.random(-100, 100), Math.random(-100, 100), Math.random(0.1)),
	size=Math.random(1, 50)
){
	// console.log({x, y})
	/*const color = Math.random() < 0.1?
		new Vector4(...hslToRgb(Math.random(), 1, 0.5), 1):
		new Vector4(1, 1, 1, 1);*/
	
	starsAddition.push({
		pos: new Vector3(x, y, 1/size),
		velocity,
		color: new Vector4(1, 1, 1, 1),
		startTime: +new Date() - t0,
		duration: Math.random(0.1, 3)
	});
	// console.log(stars[stars.length-1])
}

const last = {
	x: null,
	y: null
};
const V_RATE = 5;
const S_RATE = 3;

//事件处理
function eventHandle(x, y, end=false){
	if (last.x !== null && last.y !== null){ //前次有事件
		const dx = x - last.x,
			dy = y - last.y;
		const velocity = new Vector3(dx*V_RATE, dy*V_RATE, Math.random(0.1));
		const size = Math.sqrt(dx*dx + dy*dy) * S_RATE;
		addStar(x, y, velocity, size);
	}else{ //第一次事件
		addStar(x, y);
	}
	
	if (!end){ //事件未结束
		last.x = x, last.y = y;
	}else{ //事件结束
		last.x = last.y = null;
	}
}

//鼠标事件
window.addEventListener("mousedown", (e)=>{
	const {clientX: x, clientY: y} = e;
	eventHandle(x, y);
});
window.addEventListener("mousemove", (e)=>{
	const {clientX: x, clientY: y} = e;
	eventHandle(x, y);
});
window.addEventListener("mouseup", (e)=>{
	const {clientX: x, clientY: y} = e;
	eventHandle(x, y, true);
});

//触屏事件
window.addEventListener("touchstart", (e)=>{
	for (const touch of e.targetTouches){
		const {clientX: x, clientY: y} = touch;
		eventHandle(x, y);
	}
});
window.addEventListener("touchmove", (e)=>{
	for (const touch of e.targetTouches){
		const {clientX: x, clientY: y} = touch;
		eventHandle(x, y);
	}
});
window.addEventListener("touchend", (e)=>{
	for (const touch of e.changedTouches){
		const {clientX: x, clientY: y} = touch;
		eventHandle(x, y, true);
	}
});
window.addEventListener("touchcancel", (e)=>{
	for (const touch of e.changedTouches){
		const {clientX: x, clientY: y} = touch;
		eventHandle(x, y, true);
	}
});